package cool.taomu.toolkit.stock.controller.api

import com.google.gson.Gson
import cool.taomu.toolkit.service.client.kademlia.service.ServiceClient
import cool.taomu.toolkit.service.kademlia.entity.RequestDto
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockHistory
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockList
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockListedCompanies
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockSignal
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockSignalSummary
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockSignals
import cool.taomu.toolkit.stock.dao.jooq.tables.TaomuStockTradingSignals
import cool.taomu.toolkit.stock.dao.jooq.tables.VwTaomuStockSignalSummary
import cool.taomu.toolkit.stock.entity.StockCodeEntity
import cool.taomu.toolkit.stock.entity.StockHistoryEntity
import cool.taomu.toolkit.stock.entity.StockListedCompany
import cool.taomu.toolkit.stock.entity.StockMacdJc
import cool.taomu.toolkit.stock.service.SinajsNewChart
import cool.taomu.toolkit.stock.service.StockBuyingPointStatisticsService
import cool.taomu.toolkit.stock.service.StockBuyingPointStatisticsService.Signal
import cool.taomu.toolkit.stock.service.StockBuyingPointStatisticsService.StockRecord
import cool.taomu.toolkit.stock.service.StockSignalService
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
import java.util.ArrayList
import java.util.List
import java.util.concurrent.Callable
import javax.inject.Inject
import javax.inject.Named
import javax.servlet.http.HttpServletRequest
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.Context
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
import org.apache.commons.io.IOUtils
import org.apache.commons.vfs2.FileSystemManager
import org.joda.time.DateTime
import org.jooq.DSLContext
import org.slf4j.LoggerFactory
import redis.clients.jedis.JedisPool

import static org.jooq.impl.DSL.*
import org.eclipse.xtend.lib.annotations.Accessors
import cool.taomu.toolkit.stock.entity.TradingSignal

@Path("stock/api")
class StockDataController {

    val static LOG = LoggerFactory.getLogger(StockDataController);

    @Inject
    @Named("taomu.http.pathSpec")
    protected String pathSpec;

    @Inject
    @Named("taomu.title")
    protected String title

    @Inject
    DSLContext dsl;

    @Inject
    FileSystemManager fsManager

    @Inject
    StockBuyingPointStatisticsService service;

    @Inject
    ServiceClient scservice;

    @Inject
    cool.taomu.toolkit.Context context;

    def _2(Double number) {
        var BigDecimal bd = new BigDecimal(number);
        bd.setScale(2, RoundingMode.HALF_UP).doubleValue; // 四舍五入
    }
    
    @Inject
    SinajsNewChart newChart;

    @Accessors
    static class StockChart {
        String daily
        String min;
    }
    
    
    @GET
    @Path("/deviation/{code}")
    @Produces(MediaType.APPLICATION_JSON)
    def Response deviation(@PathParam("code") String code) {
        var dev = dsl.selectFrom(TaomuStockTradingSignals.TAOMU_STOCK_TRADING_SIGNALS).where(
            TaomuStockTradingSignals.TAOMU_STOCK_TRADING_SIGNALS.CODE.eq(code)
        ).fetchInto(TradingSignal);
        return Response.ok(dev).build
    }

    @GET
    @Path("/newchart/{code}")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getNewchart(@PathParam("code") String code) {
        if (code.startsWith("sh.") || code.startsWith("sz.")) {
            var codes = code.replace(".", "")
            var sc = new StockChart()
            var codesgif = #[codes,".gif"].join("")
            var daily = newChart.getDaily(codesgif);
            var min = newChart.getMin(codesgif);
            sc.daily = daily
            sc.min = min;
            return Response.ok(sc).build
        }
        return Response.status(404).build
    }

    @GET
    @Path("/listed/companies/{code}")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockInfo(@PathParam("code") String code) {
        if (code.startsWith("sh.") || code.startsWith("sz.")) {
            var codes = code.replace("sh.", "").replace("sz.", "");
            var infos = dsl.selectFrom(TaomuStockListedCompanies.TAOMU_STOCK_LISTED_COMPANIES).where(
                TaomuStockListedCompanies.TAOMU_STOCK_LISTED_COMPANIES.STOCK_CODE.eq(codes)).fetchInto(
                StockListedCompany)

            return Response.ok(infos).build
        } else {
            var infos = dsl.selectFrom(TaomuStockListedCompanies.TAOMU_STOCK_LISTED_COMPANIES).where(
                TaomuStockListedCompanies.TAOMU_STOCK_LISTED_COMPANIES.STOCK_CODE.eq(code)).fetchInto(
                StockListedCompany)
            return Response.ok(infos).build
        }
    }

    @GET
    @Path("/history/{code}")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockHistory(@PathParam("code") String code) {
        var res = dsl.selectFrom(TaomuStockHistory.TAOMU_STOCK_HISTORY).where(
            TaomuStockHistory.TAOMU_STOCK_HISTORY.CODE.eq(code)).fetchInto(StockHistoryEntity);
        val stocks = newArrayList()
        LOG.info("获取 {} 股票历史数据:{}", code, res.size);
        res.forEach [
            stocks.add(#[it.date, it.open, it.close, '', '', it.high, it.low, it.m5._2, it.m10._2, it.m20._2, it.m30._2,
                it.m60._2])
        ]
        return Response.ok(stocks).build
    }

    @Inject
    JedisPool pool

    String queueName = "stock_task_queue";

    @GET
    @Path("/signal/all")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockSignal(@Context HttpServletRequest request) {
        dsl.deleteFrom(TaomuStockSignal.TAOMU_STOCK_SIGNAL).execute;
        var codes = dsl.select(TaomuStockList.TAOMU_STOCK_LIST.CODE).from(TaomuStockList.TAOMU_STOCK_LIST).fetch();
        val List<RequestDto> list = codes.map [
            var dto = scservice.getRequestDto(request);
            dto.serviceName = "/stock/signal/20/" + it.get("code", String)
            return dto
        ]
        if (pool !== null) {
            try(val jedis = pool.resource) {
                list.forEach [
                    // 使用 RPUSH 将消息推送到队列尾部 (RPush)
                    // 也可以使用 LPUSH 推送到头部 (LPush)
                    var String message = new Gson().toJson(it)
                    var long result = jedis.rpush(queueName, message);
                    LOG.info("消息已推送: '" + message + "', 队列当前长度: " + result);
                ]
            } catch (Exception e) {
                LOG.error("推送消息失败: " + e.getMessage());
            }
        } else {
            list.forEach [
                scservice.execute(it.serviceName, it);
            ]
        }
        return Response.ok().build
    }

    @GET
    @Path("/signal/deviation/all")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockDeviationSignal(@Context HttpServletRequest request) {
        dsl.deleteFrom(TaomuStockTradingSignals.TAOMU_STOCK_TRADING_SIGNALS).execute;
        var codes = dsl.select(TaomuStockList.TAOMU_STOCK_LIST.CODE).from(TaomuStockList.TAOMU_STOCK_LIST).fetch();
        var List<RequestDto> list = codes.map [
            var dto = scservice.getRequestDto(request);
            dto.serviceName = "/stock/signal/deviation/15/" + it.get("code", String)
            return dto
        ]
        list.forEach [
            scservice.execute(it.serviceName, it);
        ]
        /*  var list2 = scservice.execute(list).map [
         *  	if(it.present)
         * 				ByteBufferUtils.toString(it.get().data, "UTF-8")
         ]*/
        return Response.ok().build
    }

    @GET
    @Path("/signal/vw/to/table")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockVmToTable(@Context HttpServletRequest request) {
        dsl.deleteFrom(TaomuStockSignalSummary.TAOMU_STOCK_SIGNAL_SUMMARY).execute;
        context.executor.submit([
            dsl.insertInto(TaomuStockSignalSummary.TAOMU_STOCK_SIGNAL_SUMMARY).select(
                selectFrom(VwTaomuStockSignalSummary.VW_TAOMU_STOCK_SIGNAL_SUMMARY)).execute
        ])
        return Response.ok().build
    }

    @GET
    @Path("/signal/get")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockSignal2(@Context HttpServletRequest request) {

        return Response.ok().build
    }

    @GET
    @Path("/signal/{code}")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockSignal2Code(@QueryParam("code") String code, @Context HttpServletRequest request) {
        // var res = scservice.execute("/stock/signal/5/" + code, request)
        return Response.ok().build
    }

    @GET
    @Path("/code/all")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockCodeAll() {
        var res = dsl.selectFrom(TaomuStockList.TAOMU_STOCK_LIST).fetchInto(StockCodeEntity);
        LOG.info("获取所有股票代码与名称:{}", res.size)
        return Response.ok(res).build
    }

    @GET
    @Path("/code/llm")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockCode() {
        var day = DateTime.now().minusDays(1).toString("yyyy-MM-dd'T'00:00:00");
        var lowToDouble = TaomuStockHistory.TAOMU_STOCK_HISTORY.LOW.cast(Double);
        var subSql = dsl.select(TaomuStockHistory.TAOMU_STOCK_HISTORY.CODE).from(TaomuStockHistory.TAOMU_STOCK_HISTORY).
            where(
                TaomuStockHistory.TAOMU_STOCK_HISTORY.DATE.eq(LocalDateTime.parse(day))
            ).and(lowToDouble.gt(TaomuStockHistory.TAOMU_STOCK_HISTORY.M5)).and(
                lowToDouble.gt(TaomuStockHistory.TAOMU_STOCK_HISTORY.M10)).and(
                lowToDouble.gt(TaomuStockHistory.TAOMU_STOCK_HISTORY.M20)).and(
                lowToDouble.gt(TaomuStockHistory.TAOMU_STOCK_HISTORY.M30)).and(
                lowToDouble.gt(TaomuStockHistory.TAOMU_STOCK_HISTORY.M60)).orderBy(lowToDouble.desc);
        var res = dsl.select().from(TaomuStockList.TAOMU_STOCK_LIST).where(
            TaomuStockList.TAOMU_STOCK_LIST.CODE.in(subSql)).fetchInto(StockCodeEntity);
        LOG.info("获取K线最低价高于MA:{}", res.size);
        return Response.ok(res).build
    }

    @Inject
    StockSignalService stockSignalService

    @GET
    @Path("/code/llm2")
    @Produces(MediaType.APPLICATION_JSON)
    def Response getStockCode2() {
        var res2 = stockSignalService.llm2
        return Response.ok(res2).build
    }

    @GET
    @Path("/code/macdjc")
    @Produces(MediaType.APPLICATION_JSON)
    @Deprecated
    def Response getMacdJc() {
        var day = DateTime.now().minusDays(1).toString("yyyy-MM-dd");
        try(var fobject = fsManager.resolveFile("res://template/sql/macdjc.sql")) {
            var sql = IOUtils.toString(fobject.content.inputStream, "UTF-8");
            var res = dsl.fetch(sql).into(StockMacdJc);
            LOG.info("获取MACD金叉股票个数:{}", res.size);
            return Response.ok(res).build
        }
    }

    @GET
    @Path("/buying/point/{x}/{n}")
    @Produces(MediaType.APPLICATION_JSON)
    @Deprecated
    def Response getBuyingPoint(@PathParam("x") int xDays, @PathParam("n") int nDays) {
        // 查询近 x 个天的数据并按日期排序
        val LocalDateTime endDate = LocalDateTime.now();
        val LocalDateTime startDate = endDate.minus(xDays, ChronoUnit.DAYS);
        dsl.deleteFrom(TaomuStockSignals.TAOMU_STOCK_SIGNALS).execute();
        var codes = dsl.select(TaomuStockList.TAOMU_STOCK_LIST.CODE).from(TaomuStockList.TAOMU_STOCK_LIST).fetch();
        val callables = new ArrayList<Callable<Object>>();
        codes.forEach [
            callables.add = new Callable() {
                override call() throws Exception {
                    var code = it.get("code", String)
                    var res = dsl.select().from(TaomuStockHistory.TAOMU_STOCK_HISTORY).where(
                        TaomuStockHistory.TAOMU_STOCK_HISTORY.CODE.eq(code)
                    ).and(
                        TaomuStockHistory.TAOMU_STOCK_HISTORY.DATE.greaterOrEqual(startDate)
                    ).and(TaomuStockHistory.TAOMU_STOCK_HISTORY.DATE.lessOrEqual(endDate)).orderBy(
                        TaomuStockHistory.TAOMU_STOCK_HISTORY.DATE.asc()).fetch().map([ record |
                        new StockRecord(
                            record.get("date", LocalDateTime),
                            record.get("code", String),
                            record.get("M5", Double),
                            record.get("M10", Double),
                            record.get("M20", Double),
                            record.get("macd_dif", Double),
                            record.get("macd_dea", Double),
                            record.get("close", Double),
                            record.get("low", Double),
                            record.get("BBI", Double),
                            record.get("boll_upper", Double),
                            record.get("boll_lower", Double),
                            record.get("EMA5", Double),
                            record.get("EMA10", Double),
                            record.get("rsi6", Double),
                            record.get("k", Double),
                            record.get("d", Double),
                            record.get("j", Double)
                        )
                    ]);
                    LOG.info("code:{}=>size:{}", code, res.size);
                    if (res.size > 0) {
                        // 计算 MACD 买入点及对应的其他指标买入点
                        var List<Signal> signals = service.findMacdAndOtherSignals(res, nDays);
                        LOG.info("code:{}=>signals size:{}", code, signals.size);
                        // 将结果存入数据库
                        signals.forEach [ signal |
                            dsl.insertInto(TaomuStockSignals.TAOMU_STOCK_SIGNALS).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.DATE, signal.date).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.CODE, signal.code).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.SIGNAL_TYPE, signal.signalType).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.SIGNAL_DATE, signal.signalDate).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.SIGNAL_PRICE, signal.price).set(
                                TaomuStockSignals.TAOMU_STOCK_SIGNALS.TYPE, signal.type).execute();
                        ]
                    }
                    return 1;
                }
            }
        ]
        LOG.info("callables size:{}", callables.size);
        // ExecutorUtils.submit(context.executor, callables, null, null);
        System.out.println("Signals processed and stored successfully.");
        Response.ok().build;
    }

}
